﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Extensions.DateTimeExtension
{
    public static class DateTimeExtensions
    {
        /// <summary>
        /// Returns the Datetime of Monday in this Week.
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static DateTime FirstDayOfWeek(this DateTime time)
        {
            if (time.DayOfWeek != DayOfWeek.Monday)
            {
                return time.AddDays(ReturnDayNumberFirst(time.DayOfWeek));
            }
            return time;
        }

        /// <summary>
        /// Returns the Datetime of Sunday in this Week.
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static DateTime LastDayOfWeek(this DateTime time)
        {
            if (time.DayOfWeek != DayOfWeek.Sunday)
            {
                return time.AddDays(ReturnDayNumberLast(time.DayOfWeek));
            }
            return time;
        }

        /// <summary>
        /// Returns the first Day of the month as a DateTime object
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static DateTime FirstDayOfMonth(this DateTime time)
        {
            return new DateTime(time.Year, time.Month, 1);
        }

        /// <summary>
        /// Returns the first occurance of day in the month as a DateTime object 
        /// </summary>
        /// <param name="time"></param>
        /// <param name="day"></param>
        /// <returns></returns>
        public static DateTime FirstDayOfMonth(this DateTime time, DayOfWeek day)
        {
            DateTime FirstDayOfThisMonth = new DateTime(time.Year, time.Month, 1);
            while (FirstDayOfThisMonth.DayOfWeek != day)
            {
                FirstDayOfThisMonth.AddDays(1);
            }
            return FirstDayOfThisMonth;
        }

        /// <summary>
        /// Returns the last day of the Month as a DateTime object 
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static DateTime LastDayOfMonth(this DateTime time)
        {
            return new DateTime(time.Year, time.Month, 1).AddMonths(1).AddDays(-1);
        }

        /// <summary>
        /// Returns the last occurance of day in the month as a DateTime object 
        /// </summary>
        /// <param name="time"></param>
        /// <param name="day"></param>
        /// <returns></returns>
        public static DateTime LastDayOfMonth(this DateTime time, DayOfWeek day)
        {
            DateTime lastDayOfThisMonth = new DateTime(time.Year, time.Month, 1).AddMonths(1).AddDays(-1);
            while (lastDayOfThisMonth.DayOfWeek != day)
            {
                lastDayOfThisMonth.AddDays(-1);
            }
            return lastDayOfThisMonth;
        }

        /// <summary>
        /// Return the First Day of the Year as a Datetime object
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static DateTime FirstDayOfYear(this DateTime time)
        {
            DateTime result = new DateTime(time.Year, 1, 1);
            return result;
        }

        /// <summary>
        /// Return the First Day of a specified Year as a Datetime object
        /// </summary>
        /// <param name="time"></param>
        /// <param name="year"></param>
        /// <returns></returns>
        public static DateTime FirstDayOfYear(this DateTime time, int year)
        {
            DateTime result = new DateTime(year, 1, 1); ;
            return result;
        }

        /// <summary>
        /// Returns the last Day of the Year as a Datetime object
        /// </summary>
        /// <param name="time"></param>
        /// <returns></returns>
        public static DateTime LastDayOfYear(this DateTime time)
        {
            DateTime result = new DateTime(time.Year, 12, 31);
            return result;
        }

        /// <summary>
        /// Return the Last Day of a specified Year as a Datetime object
        /// </summary>
        /// <param name="time"></param>
        /// <param name="year"></param>
        /// <returns></returns>
        public static DateTime LastDayOfYear(this DateTime time, int year)
        {
            DateTime result = new DateTime(year, 12, 31); ;
            return result;
        }

        /// <summary>
        /// Returns the number of months between the actual DateTime and the hand over DateTime
        /// </summary>
        /// <param name="time"></param>
        /// <param name="compare"></param>
        /// <returns></returns>
        public static int MonthsBetween(this DateTime time, DateTime compare)
        {
            int years = compare.Year - time.Year;
            DateTime dt = time.AddYears(years);
            if (dt > compare)
            {
                years--;
            }
            int month = compare.Month - time.Month;
            if (compare.Day < time.Day)
            {
                month--;
            }
            month = (month + 12) % 12 + years * 12;
            return month;
        }

        /// <summary>
        /// Returns the number of Days between the actual DateTime and the hand over DateTime
        /// </summary>
        /// <param name="time"></param>
        /// <param name="compare"></param>
        /// <returns></returns>
        public static int DaysBetween(this DateTime time, DateTime compare)
        {
            TimeSpan result = time - compare;
            return result.Days;
        }

        /// <summary>
        /// Returns the years between the actual DateTime and the hand over DateTime
        /// </summary>
        /// <param name="time"></param>
        /// <param name="compare"></param>
        /// <returns></returns>
        public static int YearsBetween(this DateTime time, DateTime compare)
        {
            int years = time.Year - compare.Year;
            if (years < 0)
            {
                years = years * -1;
            }
            return years;
        }

        private static int ReturnDayNumberFirst(DayOfWeek day)
        {
            switch (day)
            {
                case DayOfWeek.Friday:
                    return -4;
                case DayOfWeek.Saturday:
                    return -5;
                case DayOfWeek.Sunday:
                    return -6;
                case DayOfWeek.Thursday:
                    return -3;
                case DayOfWeek.Tuesday:
                    return -1;
                case DayOfWeek.Wednesday:
                    return -2;
                default:
                    return 0;
            }
        }

        private static int ReturnDayNumberLast(DayOfWeek day)
        {
            switch (day)
            {
                case DayOfWeek.Friday:
                    return 2;
                case DayOfWeek.Monday:
                    return 6;
                case DayOfWeek.Saturday:
                    return 1;
                case DayOfWeek.Thursday:
                    return 3;
                case DayOfWeek.Tuesday:
                    return 5;
                case DayOfWeek.Wednesday:
                    return 4;
                default:
                    return 0;
            }
        }
    }
}
